#include "bsp_uart_diver.h"
#include <stdbool.h>
#include <stdarg.h>

/**
 * @brief USART硬件及缓冲区句柄结构体
 *
 * @author 陈泳行
 * @date 2025-6-20
 */
typedef struct
{
    USART_TypeDef 	   *uartx;      ///< 指向具体USART外设的指针
    CircularBuffer_t   *cb;         ///< 指向环形缓冲区的指针
} Usart_t;

/**
 * @brief 全局串口句柄数组
 *
 * 用于管理各个串口的硬件和缓冲区资源。
 */
Usart_t g_usart_handle[UART_MAX] = {
    [1] = USART1,
    [2] = USART2,
    [3] = USART3,
    [4] = UART4,
    [6] = USART6,
};

/**
 * @brief 处理串口ORE溢出错误
 *
 * 检查并清除指定串口的ORE（溢出错误）标志位。
 *
 * @param[in] uartx 串口号
 */
void ore_error(uint8_t uartx)
{
    if((g_usart_handle[uartx].uartx->SR & USART_SR_ORE) != RESET)
    {
        g_usart_handle[uartx].uartx->SR; // 读取状态寄存器清除标志
        g_usart_handle[uartx].uartx->DR; // 读取数据寄存器清除标志
    }		
}

/**
 * @brief 串口单字节发送
 *
 * 发送一个字节数据到指定串口。
 *
 * @param[in] uartx 串口号
 * @param[in] data  要发送的数据
 * @retval 2 没有串口句柄
 * @retval 1 超时
 * @retval 0 发送成功
 */
char uart_send_byte(uint8_t uartx, uint8_t data)
{
    USART_TypeDef *USARTx = g_usart_handle[uartx].uartx;
    if(USARTx == NULL) return 2;
    uint16_t time = 0xFFFF;
    while((USARTx->SR & UART_FLAG_TXE) == 0 && --time)
        if(!time) return 1;
    USARTx->DR = data;
    return 0;
}

/**
 * @brief 串口字符串发送
 *
 * 发送以'\0'结尾的字符串到指定串口，自动处理换行符。
 *
 * @param[in] uartx 串口号
 * @param[in] str   字符串指针
 * @retval 1 发送失败
 * @retval 0 发送成功
 */
char uart_send_str(uint8_t uartx, const char* str)
{
    while(*str)
    {
        if(*str == '\n')
            uart_send_byte(uartx, '\r');
        if(0 == uart_send_byte(uartx, (uint8_t)*str))
            str++;
        else
            return 1;
    }
    return 0;
}

/**
 * @brief 数据流发送
 *
 * 发送指定长度的数据流到串口。
 *
 * @param[in] uartx 串口号
 * @param[in] data  数据指针
 * @param[in] len   数据长度
 * @retval 1 发送失败
 * @retval 0 发送成功
 */
char uart_send_data(uint8_t uartx, uint8_t* data, uint16_t len)
{
    while(len--)
    {
        if(0 == uart_send_byte(uartx, (uint8_t)*data))
            data++;
        else
            return 1;
    }
    return 0;
}

/**
 * @brief 串口重复利用检测
 *
 * 检查指定串口号是否已被初始化，防止重复初始化。
 *
 * @param[in] num 串口号
 * @retval false 未使用
 * @retval true  已重复利用
 */
bool uart_used(uint8_t num)
{
    static uint8_t uart_num[6] = {0}; ///< 串口使用标志数组
    if(uart_num[num] == 1)
    {
        printf("发现串口%u重复利用 tick [%d]\n",
                                           num,
                                HAL_GetTick());
        return true;
    }
    uart_num[num] = 1;
    return false;
}

/**
 * @brief 串口初始化
 *
 * 初始化指定串口并绑定环形缓冲区。
 *
 * @param[in] uartx 串口号
 * @param[in] cb    环形缓冲区指针
 * @retval UART_NOUART  串口号无效
 * @retval UART_USED    串口已被使用
 * @retval UART_OTHER   缓冲区指针无效
 * @retval UART_NOERROR 初始化成功
 */
UART_ERROR uart_init(uint8_t uartx, void *cb)
{
    if(uartx > UART_MAX - 1) return UART_NOUART;
    if(uart_used(uartx - 1)) return UART_USED;
    if(cb == NULL) return UART_OTHER;

    CircularBuffer_t *cbuff = (CircularBuffer_t *)cb;
    g_usart_handle[uartx].cb = cbuff;

    switch(uartx)
    {
        case 1:
            MX_USART1_UART_Init();
            HAL_UARTEx_ReceiveToIdle_DMA(&huart1, cbuff->buffer, cbuff->size);
        break;
        case 2:
            
        break;
        case 3:
            
        break;
        case 4:
            
        break;
        case 6:
            
        break;
    }
    printf("USART%d success tick [%d]\n", uartx, HAL_GetTick());
    return UART_NOERROR;
}

/**
 * @brief 格式化输出到串口
 *
 * 类似printf，格式化字符串并通过指定串口发送。
 *
 * @param[in] huart   串口句柄指针
 * @param[in] format  格式化字符串
 * @param[in] ...     可变参数
 * @return 实际发送的字符数
 */
int my_printf(UART_HandleTypeDef *huart, const char *format, ...)
{
    char buffer[512]; // 临时存储格式化后的字符串
    va_list arg;	  // 处理可变参数
    int len;		  // 最终字符串长度
    
    va_start(arg, format);
    // 安全地格式化字符串到buffer
    len = vsnprintf(buffer, sizeof(buffer), format, arg);
    va_end(arg);

    // 通过HAL库发送buffer中的内容
    HAL_UART_Transmit(huart, (uint8_t *)buffer, (uint16_t)len, 0xFF);
    return len;
}

